package main

import (
	"context"
	"encoding/base64"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/go-redis/redis/v8"
)

// 短链接存储结构
type ShortURL struct {
	LongURL    string     `redis:"long_url"`
	CreateTime time.Time  `redis:"create_time"`
	ExpireTime *time.Time `redis:"expire_time"` // 可为空（表示永久有效）
	ClickCount int        `redis:"click_count"`
}

var (
	rdb           *redis.Client
	ctx                 = context.Background()
	idCounter     int64 = 100000 // 自增ID起始值
	base62Chars         = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"
	base62Encoder       = base64.NewEncoding(base62Chars)
)

// 初始化Redis连接
func initRedis() {
	rdb = redis.NewClient(&redis.Options{
		Addr:     "localhost:6379", // Redis默认地址
		Password: "",               // 无密码
		DB:       0,                // 默认数据库
	})

	// 测试连接
	_, err := rdb.Ping(ctx).Result()
	if err != nil {
		panic(fmt.Sprintf("Redis连接失败: %v", err))
	}
	fmt.Println("Redis连接成功")

	// 从Redis加载最新ID（避免重启后ID重置）
	val, err := rdb.Get(ctx, "id_counter").Result()
	if err == nil {
		idCounter, _ = strconv.ParseInt(val, 10, 64)
	}
}

// 生成短码（自增ID转Base62）
func generateShortCode() string {
	idCounter++
	// 同步ID到Redis（持久化）
	rdb.Set(ctx, "id_counter", idCounter, 0)

	idBytes := []byte(strconv.FormatInt(idCounter, 10))
	encoded := base62Encoder.EncodeToString(idBytes)
	encoded = strings.ReplaceAll(encoded, "-", "")
	encoded = strings.ReplaceAll(encoded, "_", "")
	return encoded
}

// 创建短链接 handler
func createShortURLHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "只支持POST方法", http.StatusMethodNotAllowed)
		return
	}

	longURL := r.FormValue("long_url")
	expireMinutesStr := r.FormValue("expire_minutes")
	expireDaysStr := r.FormValue("expire_days")

	if longURL == "" {
		http.Error(w, "长链接不能为空", http.StatusBadRequest)
		return
	}

	shortCode := generateShortCode()
	var expireTime *time.Time
	var redisExpire time.Duration = 0 // Redis过期时间（0表示永久）

	// 优先处理分钟级过期（测试用）
	if expireMinutesStr != "" {
		expireMinutes, err := strconv.Atoi(expireMinutesStr)
		if err == nil && expireMinutes >= 0 {
			et := time.Now().Add(time.Duration(expireMinutes) * time.Minute)
			expireTime = &et
			if expireMinutes > 0 {
				redisExpire = time.Duration(expireMinutes) * time.Minute
			}
		}
	} else if expireDaysStr != "" {
		expireDays, err := strconv.Atoi(expireDaysStr)
		if err == nil && expireDays > 0 {
			et := time.Now().AddDate(0, 0, expireDays)
			expireTime = &et
			redisExpire = time.Duration(expireDays) * 24 * time.Hour
		}
	}

	// 存储到Redis
	now := time.Now()
	createTimeStr := now.Format(time.RFC3339Nano)
	expireTimeStr := ""
	if expireTime != nil {
		expireTimeStr = expireTime.Format(time.RFC3339Nano)
	}

	key := fmt.Sprintf("short_url:%s", shortCode)
	err := rdb.HSet(ctx, key, map[string]interface{}{
		"long_url":    longURL,
		"create_time": createTimeStr,
		"expire_time": expireTimeStr,
		"click_count": 0,
	}).Err()

	if err != nil {
		http.Error(w, "存储失败", http.StatusInternalServerError)
		return
	}

	// 设置Redis键过期时间
	if redisExpire > 0 {
		rdb.Expire(ctx, key, redisExpire)
	}

	shortURL := fmt.Sprintf("http://localhost:8080/%s", shortCode)
	w.Header().Set("Content-Type", "application/json")
	w.Write([]byte(fmt.Sprintf(`{"short_url": "%s", "short_code": "%s", "expire_time": "%s"}`,
		shortURL, shortCode, formatExpireTime(expireTime))))
}

// 短链接跳转 handler
func redirectHandler(w http.ResponseWriter, r *http.Request) {
	shortCode := r.URL.Path[1:]
	if shortCode == "" {
		http.Redirect(w, r, "/", http.StatusFound)
		return
	}

	key := fmt.Sprintf("short_url:%s", shortCode)
	data, err := rdb.HGetAll(ctx, key).Result()
	if err != nil || len(data) == 0 {
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte(renderErrorPage("链接不存在", "该短链接可能已被删除或从未存在")))
		return
	}

	// 解析数据
	longURL := data["long_url"]
	expireTimeStr := data["expire_time"]

	// 检查是否过期（Redis自动过期+手动检查双重保险）
	if expireTimeStr != "" {
		expireTime, _ := time.Parse(time.RFC3339Nano, expireTimeStr)
		if time.Now().After(expireTime) {
			rdb.Del(ctx, key) // 主动删除过期数据
			w.Header().Set("Content-Type", "text/html; charset=utf-8")
			w.WriteHeader(http.StatusGone)
			w.Write([]byte(renderExpiredPage(expireTime.Format("2006-01-02 15:04:05"))))
			return
		}
	}

	// 更新访问计数
	rdb.HIncrBy(ctx, key, "click_count", 1)

	// 302重定向
	http.Redirect(w, r, longURL, http.StatusFound)
}

// 查看短链接统计 handler
func statsHandler(w http.ResponseWriter, r *http.Request) {
	shortCode := r.URL.Query().Get("code")
	if shortCode == "" {
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(renderErrorPage("参数错误", "请提供短码参数，例如：/stats?code=abc123")))
		return
	}

	key := fmt.Sprintf("short_url:%s", shortCode)
	data, err := rdb.HGetAll(ctx, key).Result()
	if err != nil || len(data) == 0 {
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte(renderErrorPage("链接不存在", "该短链接可能已被删除或从未存在")))
		return
	}

	// 解析数据
	longURL := data["long_url"]
	clickCount, _ := strconv.Atoi(data["click_count"])
	createTime, _ := time.Parse(time.RFC3339Nano, data["create_time"])
	expireTimeStr := data["expire_time"]

	// 处理过期时间显示
	expireDisplay := "永久有效"
	if expireTimeStr != "" {
		expireTime, _ := time.Parse(time.RFC3339Nano, expireTimeStr)
		expireDisplay = expireTime.Format("2006-01-02 15:04:05")
		if time.Now().After(expireTime) {
			expireDisplay += "（已过期）"
		}
	}

	statsHTML := fmt.Sprintf(`
	<!DOCTYPE html>
	<html lang="zh-CN">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>短链接统计 - %s</title>
		<style>
			body {
				font-family: 'Arial', sans-serif;
				max-width: 800px;
				margin: 2rem auto;
				padding: 0 1rem;
				background-color: #f8f9fa;
			}
			.container {
				background: white;
				padding: 2rem;
				border-radius: 10px;
				box-shadow: 0 4px 20px rgba(0,0,0,0.1);
			}
			h1 {
				color: #2c3e50;
				border-bottom: 2px solid #3498db;
				padding-bottom: 0.5rem;
			}
			.stats-item {
				margin: 1rem 0;
				padding: 0.8rem;
				background-color: #f1f5f9;
				border-radius: 5px;
			}
			.label {
				font-weight: bold;
				color: #2c3e50;
			}
			.value {
				margin-top: 0.3rem;
				color: #34495e;
			}
			.button {
				display: inline-block;
				margin-top: 1rem;
				padding: 0.6rem 1.5rem;
				background-color: #3498db;
				color: white;
				text-decoration: none;
				border-radius: 5px;
				transition: background-color 0.3s;
			}
			.button:hover {
				background-color: #2980b9;
			}
		</style>
	</head>
	<body>
		<div class="container">
			<h1>短链接统计信息</h1>
			
			<div class="stats-item">
				<div class="label">短码</div>
				<div class="value">%s</div>
			</div>
			
			<div class="stats-item">
				<div class="label">原始长链接</div>
				<div class="value"><a href="%s" target="_blank">%s</a></div>
			</div>
			
			<div class="stats-item">
				<div class="label">创建时间</div>
				<div class="value">%s</div>
			</div>
			
			<div class="stats-item">
				<div class="label">过期时间</div>
				<div class="value">%s</div>
			</div>
			
			<div class="stats-item">
				<div class="label">访问次数</div>
				<div class="value">%d 次</div>
			</div>
			
			<a href="/%s" class="button" target="_blank">访问短链接</a>
			<a href="/" class="button" style="margin-left: 1rem;">返回首页</a>
		</div>
	</body>
	</html>
	`, shortCode, shortCode, longURL, longURL,
		createTime.Format("2006-01-02 15:04:05"),
		expireDisplay, clickCount, shortCode)

	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	w.Write([]byte(statsHTML))
}

// 格式化过期时间显示
func formatExpireTime(t *time.Time) string {
	if t == nil {
		return "永久有效"
	}
	return t.Format("2006-01-02 15:04:05")
}

// 渲染过期页面
func renderExpiredPage(expireTime string) string {
	return fmt.Sprintf(`
	<!DOCTYPE html>
	<html lang="zh-CN">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>链接已过期</title>
		<style>
			body {
				font-family: 'Arial', sans-serif;
				display: flex;
				flex-direction: column;
				align-items: center;
				justify-content: center;
				height: 100vh;
				margin: 0;
				background-color: #f8f9fa;
				color: #333;
			}
			.container {
				text-align: center;
				background: white;
				padding: 2rem 4rem;
				border-radius: 10px;
				box-shadow: 0 4px 20px rgba(0,0,0,0.1);
			}
			.icon {
				font-size: 5rem;
				color: #e74c3c;
				margin-bottom: 1rem;
			}
			h1 {
				color: #e74c3c;
				margin-bottom: 0.5rem;
			}
			.info {
				font-size: 1.1rem;
				margin: 1rem 0 2rem;
				line-height: 1.6;
			}
			.button {
				display: inline-block;
				padding: 0.8rem 2rem;
				background-color: #3498db;
				color: white;
				text-decoration: none;
				border-radius: 5px;
				transition: background-color 0.3s;
			}
			.button:hover {
				background-color: #2980b9;
			}
		</style>
	</head>
	<body>
		<div class="container">
			<div class="icon">⌛</div>
			<h1>链接已过期</h1>
			<div class="info">
				<p>该短链接已于 <strong>%s</strong> 过期</p>
				<p>请联系链接提供者获取最新地址</p>
			</div>
			<a href="/" class="button">返回首页</a>
		</div>
	</body>
	</html>
	`, expireTime)
}

// 渲染通用错误页面
func renderErrorPage(title, message string) string {
	return fmt.Sprintf(`
	<!DOCTYPE html>
	<html lang="zh-CN">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>%s</title>
		<style>
			body {
				font-family: 'Arial', sans-serif;
				display: flex;
				flex-direction: column;
				align-items: center;
				justify-content: center;
				height: 100vh;
				margin: 0;
				background-color: #f8f9fa;
				color: #333;
			}
			.container {
				text-align: center;
				background: white;
				padding: 2rem 4rem;
				border-radius: 10px;
				box-shadow: 0 4px 20px rgba(0,0,0,0.1);
			}
			.icon {
				font-size: 5rem;
				color: #e74c3c;
				margin-bottom: 1rem;
			}
			h1 {
				color: #e74c3c;
				margin-bottom: 0.5rem;
			}
			.info {
				font-size: 1.1rem;
				margin: 1rem 0 2rem;
			}
			.button {
				display: inline-block;
				padding: 0.8rem 2rem;
				background-color: #3498db;
				color: white;
				text-decoration: none;
				border-radius: 5px;
				transition: background-color 0.3s;
			}
			.button:hover {
				background-color: #2980b9;
			}
		</style>
	</head>
	<body>
		<div class="container">
			<div class="icon">⚠️</div>
			<h1>%s</h1>
			<div class="info">%s</div>
			<a href="/" class="button">返回首页</a>
		</div>
	</body>
	</html>
	`, title, title, message)
}

// 首页
func homeHandler(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != "/" {
		redirectHandler(w, r)
		return
	}
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	w.Write([]byte(`
	<!DOCTYPE html>
	<html>
	<head>
		<title>短链接服务</title>
		<style>
			body { max-width: 800px; margin: 2rem auto; padding: 0 1rem; font-family: sans-serif; }
			.form-group { margin: 1rem 0; }
			input { padding: 0.5rem; width: 100%; max-width: 500px; }
			button { padding: 0.5rem 2rem; background: #3498db; color: white; border: none; border-radius: 4px; cursor: pointer; }
			.stats-link { margin-top: 1rem; display: block; }
		</style>
	</head>
	<body>
		<h1>创建短链接</h1>
		<form method="post" action="/create">
			<div class="form-group">
				<label>长链接：</label><br>
				<input type="url" name="long_url" placeholder="https://example.com" required>
			</div>
			<div class="form-group">
				<label>过期时间（分钟，测试用）：</label><br>
				<input type="number" name="expire_minutes" placeholder="0表示永久，1表示1分钟后过期" value="1">
			</div>
			<button type="submit">生成短链接</button>
		</form>
		<a href="/stats" class="stats-link">查看短链接统计（需提供code参数）</a>
	</body>
	</html>
	`))
}

func main() {
	// 初始化Redis
	initRedis()
	defer rdb.Close()

	// 注册路由
	http.HandleFunc("/create", createShortURLHandler)
	http.HandleFunc("/stats", statsHandler)
	http.HandleFunc("/", homeHandler)

	fmt.Println("服务启动成功，监听端口：8080")
	fmt.Println("使用示例：")
	fmt.Println("1. 访问首页创建短链接：http://localhost:8080")
	fmt.Println("2. 访问短链接：http://localhost:8080/[短码]")
	fmt.Println("3. 查看统计：http://localhost:8080/stats?code=[短码]")
	http.ListenAndServe(":8080", nil)
}
